typescript

[TS] 3. Function in TypeScript

9 min read|17. 12. 18.

typescript_banner

TypeScript에서 함수를 정의하는데 있어서 몇 가지 추가된 기능에 대해 살펴봅니다.

Table of Contents

  • Return type, Parameter type
  • Default Parameter / Rest Parameter
  • Optional Parameter
  • Union Type
  • Overloading

인자와 반환값의 타입을 설정한다.

함수 또는 메소드를 정의할 때, 타입을 정의해줍니다. ES6 code

function getMonthFromString(dateOfStringFormat) {
  const monthOfNumberFormat = parseInt(dateOfStringFormat.substring(4, 6), 10)
  return monthOfNumberFormat
}

위 함수는 '201712'이라는 문자열을 받아서 해당하는 월을 반환하는 함수입니다. (반환값의 형식을 명시하기 위해 바로 return하지 않고 변수에 임시로 받아준 뒤 반환합니다.) 위와 같이 String 타입의 인자를 받아야 한다는 것을 명시해줘야 하기 때문에 변수명부터 굉장히 이상해집니다. 자바스크립트에는 타입이라는 것이 없기 때문에 메소드명 또는 변수명에 타입을 명시할 수 밖에 없습니다.

만약 Number 타입의 201712를 인자로 넘겨준다면 Number에는 substring이라는 함수가 없기 때문에 에러가 발생합니다. 위 함수를 보다 안정적으로 작성하기 위해서는 다음과 같은 if 문이 필요하게 됩니다.

function getMonthFromString(dateOfStringFormat) {
  if (typeof dateOfStringFormat !== 'string') {
    throw Error('Invalid format of parameter')
  }
  const monthOfNumberFormat = parseInt(dateOfStringFormat.substring(4, 6), 10)
  return monthOfNumberFormat
}

기본적으로 함수가 수행해야하는 비즈니스 로직 외 불필요한 방어코드가 코드를 더럽히고 있습니다. 위 함수를 TypeScript 함수로 변경해보겠습니다.

const getMonth = (date: string): number => {
  return parseInt(date.substring(4, 6), 10)
}

인자와 반환값에 타입을 설정하여 메소드 이름과 변수명이 훨씬 짧아졌습니다. 그럼에도 불구하고 해당 함수가 하는 역할을 ES6로 작성했을 때보다 명확해졌으며, 불필요한 방어코드 마저 사라졌습니다. wtfjs에서 확인하실 수 있지만 자바스크립트에서는 타입이 멋대로(사실은 매우 다양한 규칙을 기반으로) 캐스팅되는 경우가 많은데요, 이를 방지하기 위해 우리는 불필요한 방어코드를 작성해왔습니다. 타입을 지정함으로써 이러한 작업을 최소화 할 수 있습니다.

Default Parameter, Rest Parameter

해당 스펙은 ES6 표준 스펙에서도 지원하고 있는 스펙이므로 구체적은 설명은 넘어가겠습니다. 자세한 내용은 첨부하는 포스팅을 확인해주세요.

TypeScript에서도 해당 스펙을 지원합니다.

Default parameter TypeScript code

const getRandomNumber = (min: number = 0, max: number = 10): number => {
  return Math.floor(Math.random() * (max - min)) + min
}

console.log(getRandomNumber(1)) // OK!

위 코드에서는 인자가 넘겨지지 않았을 경우(undefined), 지정해준 값으로 인자를 설정합니다. 명시적으로 null을 인자로 넘겨주면 default로 설정된 parameter를 무시하고 null을 인자로 넘깁니다. 지정한 인자를 모두 넘기지 않으면 에러를 뱉던 TypeScript도 default parameter가 지정되어 있으면 에러를 발생시키지 않습니다.

Rest parameter TypeScript code

export const setSkills = (...skills: string[]): void => {
  // ...
}

rest parameter의 타입은 배열(array)이므로 인자에 해당하는 타입을 설정해줍니다.

Optional Parameter

TypeScript에서는 default parameter 없을 경우, signature에서 정의한대로 인자를 넘겨주지 않으면 에러가 발생합니다. 하지만 파라미터를 넘겨주지 않아도 되도록 설정할 수 있습니다.

const setSpec = (major: string, option?: string): void => {
  console.log(major)
  console.log(option)
}
setSpec('Computer Science')
// console> Computer Science
// console> undefined

?를 통해서 함수에 optional한 parameter를 지정할 수 있습니다.

Union Type

파라미터에 타입을 지정할 때, 두 가지 이상의 타입이 지정할 필요가 있을 수 있는데요, 그럴 때 Union type을 통해서 파라미터의 타입을 지정해줄 수 있습니다.

sayName(position: string | boolean | number): void {
  if (typeof position === "string") {
    console.log(`string type position`);
  } else if (typeof position === "boolean") {
    console.log(`boolean type position`)
  } else {
    console.log(`else`);
  }
}

위 코드에서는 position이라는 파라미터가 string, boolean, number 세 가지의 타입일 수 있다고 signature를 지정했습니다. (예제가 송구스럽네요)

Overloading

바로 이전 Class 포스팅에서 자바와 같은 오버로딩을 지원하지 않는다고 했는데요, optional parameterunion type 그리고 any라는 타입을 사용하면 자바에서 구현하는 것과는 조금 다르지만 오버로딩을 구현할 수 있습니다.

// Person.ts

class Person {
  //..
  sayName(position: string, option?: string): void
  sayName(position: boolean, option: string): void
  sayName(position: string | boolean, option: any): any {
    if (typeof position === 'string') {
      console.log(`string type position`)
    } else if (typeof position === 'boolean') {
      console.log(`boolean type position`)
    } else {
      console.log(`else`)
    }
  }
  //..
}

위와 같이 동일한 메소드 명에 대해 여러 Signature를 정의할 수 있습니다. 위 코드에서는 sayName이라는 메소드의 Signature가 세 개이며 마지막 메소드에서만 이를 구현하고 있습니다. 그리고 메소드 body에서는 parameter의 타입으로 분기를 하여 로직을 수행하고 있습니다.

const person: Person = new Person();

person.sayName("FrontEnd");
person.sayName("FrontEnd", "optional);
person.sayName(false, "option required");
// person.sayName(true); Error! (1)
// person.sayName(1); Error! (2)

sayName을 호출하게 되면, 메소드를 호출하는 시점에서 각 상황에 맞는 signature가 적용됩니다. Error (1)을 보면 boolean 타입이 인자로 넘어갔을 경우의 signature에 따라 optionrequired 인자이므로 에러가 발생합니다. Error (2)는 어느 signature와도 일치하지 않으니 에러가 발생합니다. 이렇게 TypeScript에서는 여러 signature를 정의한 뒤 메소드 내에서 이를 분기하여 오버로딩을 구현할 수 있습니다.

마무리

TypeScript 좀 더 안정성 있는, 간결한, 가독성이 좋은 함수를 작성할 수 있게 되었습니다. 어떻게 보면 타이핑이 길어지는 결과처럼 보일 수 있겠지만 타입의 명시가 필요한 부분에 있어서는 오히려 코드가 더 짧아지게 되었습니다.

해당 포스팅 외 다른 타입스크립트 포스팅은 여기에서 보실 수 있으며 예제에 사용된 코드는 여기에서 확인하실 수 있습니다. 감사합니다.

References

TypeScript Official Document - Function